summaryrefslogtreecommitdiff
path: root/app/api/auth/[...nextauth]/route.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-08 11:23:40 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-08 11:23:40 +0000
commitb84621f9b2b7161a5ad4f0b194264e9df3e65dbf (patch)
treece5ec30b3d1e5104a3a2d942c71973779436783b /app/api/auth/[...nextauth]/route.ts
parent97936ddf280c56a4f122dedcb8dc389d0d2e63a2 (diff)
(대표님) 20250708 미반영분 커밋
Diffstat (limited to 'app/api/auth/[...nextauth]/route.ts')
-rw-r--r--app/api/auth/[...nextauth]/route.ts62
1 files changed, 41 insertions, 21 deletions
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
index e059377c..68cc3a5b 100644
--- a/app/api/auth/[...nextauth]/route.ts
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -13,15 +13,18 @@ import { verifyOtpTemp } from '@/lib/users/verifyOtp'
import { getSecuritySettings } from '@/lib/password-policy/service'
import { verifySmsToken } from '@/lib/users/auth/passwordUtil'
import { SessionRepository } from '@/lib/users/session/repository'
+import { getUserRoles } from '@/lib/users/service'
+
// 인증 방식 타입 정의
type AuthMethod = 'otp' | 'email' | 'sgips' | 'saml'
-// 모듈 보강 선언 - ID를 string으로 통일
+
+// ✅ 모듈 보강 선언 - roles 배열 추가
declare module "next-auth" {
interface Session {
user: {
- id: string // number → string으로 변경
+ id: string
name?: string | null
email?: string | null
image?: string | null
@@ -32,11 +35,12 @@ declare module "next-auth" {
authMethod?: AuthMethod
sessionExpiredAt?: number | null
dbSessionId?: string | null
+ roles?: string[] // ✅ roles 배열 추가
}
}
interface User {
- id: string // number → string으로 변경
+ id: string
imageUrl?: string | null
companyId?: number | null
techCompanyId?: number | null
@@ -44,12 +48,13 @@ declare module "next-auth" {
reAuthTime?: number | null
authMethod?: AuthMethod
dbSessionId?: string | null
+ roles?: string[] // ✅ roles 배열 추가
}
}
declare module "next-auth/jwt" {
interface JWT {
- id?: string // 이미 string이므로 그대로
+ id?: string
imageUrl?: string | null
companyId?: number | null
techCompanyId?: number | null
@@ -58,6 +63,7 @@ declare module "next-auth/jwt" {
authMethod?: AuthMethod
sessionExpiredAt?: number | null
dbSessionId?: string | null
+ roles?: string[] // ✅ roles 배열 추가
}
}
@@ -118,7 +124,7 @@ function getClientIP(req: any): string {
export const authOptions: NextAuthOptions = {
providers: [
- // OTP 로그인 - 타입 에러 수정
+ // ✅ OTP 로그인 - roles 정보 추가
CredentialsProvider({
id: 'credentials-otp',
name: 'OTP',
@@ -134,12 +140,14 @@ export const authOptions: NextAuthOptions = {
return null
}
+ // ✅ 사용자 roles 정보 조회
+ const userRoles = await getUserRoles(user.id)
+
const securitySettings = await getCachedSecuritySettings()
const reAuthTime = Date.now()
- // 반환 객체의 id를 string으로 변환
return {
- id: ensureString(user.id), // ✅ string으로 변환
+ id: ensureString(user.id),
email: user.email,
imageUrl: user.imageUrl ?? null,
name: user.name,
@@ -148,16 +156,17 @@ export const authOptions: NextAuthOptions = {
domain: user.domain,
reAuthTime,
authMethod: 'otp' as AuthMethod,
+ roles: userRoles, // ✅ roles 배열 추가
}
},
}),
- // MFA 완료 후 최종 인증 - 타입 에러 수정
+ // ✅ MFA 완료 후 최종 인증 - roles 정보 추가
CredentialsProvider({
id: 'credentials-mfa',
name: 'MFA Verification',
credentials: {
- userId: { label: 'User ID', type: 'text' }, // number → text로 변경
+ userId: { label: 'User ID', type: 'text' },
smsToken: { label: 'SMS Token', type: 'text' },
tempAuthKey: { label: 'Temp Auth Key', type: 'text' },
},
@@ -167,7 +176,6 @@ export const authOptions: NextAuthOptions = {
return null
}
- // userId를 number로 변환하여 DB 조회
const numericUserId = ensureNumber(credentials.userId)
const user = await getUserById(numericUserId)
if (!user) {
@@ -193,6 +201,9 @@ export const authOptions: NextAuthOptions = {
// 임시 인증 정보를 사용됨으로 표시
await SessionRepository.markTempAuthSessionAsUsed(credentials.tempAuthKey)
+ // ✅ 사용자 roles 정보 조회
+ const userRoles = await getUserRoles(user.id)
+
// 보안 설정 및 세션 정보 설정
const securitySettings = await getCachedSecuritySettings()
const reAuthTime = Date.now()
@@ -203,7 +214,7 @@ export const authOptions: NextAuthOptions = {
const userAgent = req.headers?.['user-agent']
const dbSession = await SessionRepository.createLoginSession({
- userId: user.id, // number로 전달
+ userId: user.id,
ipAddress,
userAgent,
authMethod: tempAuth.authMethod,
@@ -212,9 +223,8 @@ export const authOptions: NextAuthOptions = {
console.log(`MFA completed for user ${user.email} (${tempAuth.authMethod})`)
- // 반환 객체의 id를 string으로 변환
return {
- id: ensureString(user.id), // ✅ string으로 변환
+ id: ensureString(user.id),
email: user.email,
imageUrl: user.imageUrl ?? null,
name: user.name,
@@ -224,6 +234,7 @@ export const authOptions: NextAuthOptions = {
reAuthTime,
authMethod: tempAuth.authMethod as AuthMethod,
dbSessionId: dbSession.id,
+ roles: userRoles, // ✅ roles 배열 추가
}
} catch (error) {
@@ -271,6 +282,7 @@ export const authOptions: NextAuthOptions = {
},
callbacks: {
+ // ✅ JWT callback에 roles 정보 추가
async jwt({ token, user, account, trigger, session }) {
const securitySettings = await getCachedSecuritySettings()
const sessionTimeoutMs = securitySettings.sessionTimeoutMinutes * 60 * 1000
@@ -278,7 +290,7 @@ export const authOptions: NextAuthOptions = {
// 최초 로그인 시 (MFA 완료 후)
if (user) {
const reAuthTime = Date.now()
- token.id = user.id // ✅ 이제 둘 다 string 타입
+ token.id = user.id
token.email = user.email
token.name = user.name
token.companyId = user.companyId
@@ -289,16 +301,24 @@ export const authOptions: NextAuthOptions = {
token.authMethod = user.authMethod
token.sessionExpiredAt = reAuthTime + sessionTimeoutMs
token.dbSessionId = user.dbSessionId
+ token.roles = user.roles // ✅ roles 정보 추가
}
- // SAML 인증 시 DB 세션 생성
+ // SAML 인증 시 DB 세션 생성 및 roles 조회
if (account && account.provider === 'credentials-saml' && token.id) {
const reAuthTime = Date.now()
const sessionExpiredAt = new Date(reAuthTime + sessionTimeoutMs)
try {
+ const numericUserId = ensureNumber(token.id)
+
+ // ✅ SAML 로그인 시에도 roles 정보 조회
+ if (!token.roles) {
+ token.roles = await getUserRoles(numericUserId)
+ }
+
const dbSession = await SessionRepository.createLoginSession({
- userId: ensureNumber(token.id), // string을 number로 변환하여 DB에 저장
+ userId: numericUserId,
ipAddress: '0.0.0.0',
authMethod: 'saml',
sessionExpiredAt,
@@ -338,6 +358,7 @@ export const authOptions: NextAuthOptions = {
return token
},
+ // ✅ Session callback에 roles 정보 추가
async session({ session, token }: { session: Session; token: JWT }) {
// 세션 만료 체크
if (token.sessionExpiredAt && Date.now() > token.sessionExpiredAt) {
@@ -356,7 +377,7 @@ export const authOptions: NextAuthOptions = {
if (token) {
session.user = {
- id: token.id as string, // ✅ string으로 일관성 유지
+ id: token.id as string,
email: token.email as string,
name: token.name as string,
domain: token.domain as string,
@@ -367,6 +388,7 @@ export const authOptions: NextAuthOptions = {
authMethod: token.authMethod as AuthMethod,
sessionExpiredAt: token.sessionExpiredAt as number | null,
dbSessionId: token.dbSessionId as string | null,
+ roles: token.roles as string[] || [], // ✅ roles 정보 추가
}
}
return session
@@ -396,7 +418,7 @@ export const authOptions: NextAuthOptions = {
// 이미 MFA에서 DB 세션이 생성된 경우가 아니라면 여기서 생성
if (account?.provider !== 'credentials-mfa' && user.id) {
try {
- const numericUserId = ensureNumber(user.id) // string을 number로 변환
+ const numericUserId = ensureNumber(user.id)
// 기존 활성 세션 확인
const existingSession = await SessionRepository.getActiveSessionByUserId(numericUserId)
@@ -427,15 +449,13 @@ export const authOptions: NextAuthOptions = {
await SessionRepository.logoutSession(dbSessionId)
} else if (userId) {
// dbSessionId가 없는 경우 사용자의 모든 활성 세션 로그아웃
- const numericUserId = ensureNumber(userId) // string을 number로 변환
+ const numericUserId = ensureNumber(userId)
await SessionRepository.logoutAllUserSessions(numericUserId)
}
}
}
}
-
const handler = NextAuth(authOptions)
-// ✅ 핵심: 반드시 GET, POST를 named export로 내보내야 함
export { handler as GET, handler as POST } \ No newline at end of file